﻿using System;
using System.Text;
using System.Text.RegularExpressions;

namespace Theoreme.Library
{
    /// <summary>
    /// Utility class for handling random
    /// information.
    /// </summary>
    public class Random : System.Random
    {
        /// <summary>
        /// Constructor
        /// </summary>
        public Random()
            : this(DateTime.Now.Millisecond)
        {
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="seed">Seed value</param>
        public Random(int seed)
            : base(seed)
        {
        }


        /// <summary>
        /// returns a random date/time for a specific date range.
        /// </summary>
        /// <param name="start">start time</param>
        /// <param name="end">end time</param>
        /// <returns>A random date/time between the start and end times</returns>
        public virtual DateTime NextDate(DateTime start, DateTime end)
        {
            if (start > end)
                throw new ArgumentException("The start value must be earlier than the end value");
            return start + new TimeSpan((long) (new TimeSpan(end.Ticks - start.Ticks).Ticks*NextDouble()));
        }

        /// <summary>
        /// returns a randomly generated string
        /// </summary>
        /// <param name="length">length of the string</param>
        /// <returns>a randomly generated string of the specified length</returns>
        public virtual string NextString(int length)
        {
            if (length < 1)
                return "";
            return NextString(length, ".");
        }

        /// <summary>
        /// Returns a randomly generated string of a specified length, containing
        /// only a set of characters
        /// </summary>
        /// <param name="length">length of the string</param>
        /// <param name="allowedCharacters">Characters that are allowed in the string</param>
        /// <returns>A randomly generated string of the specified length, containing only the allowed characters.</returns>
        public virtual string NextString(int length, string allowedCharacters)
        {
            if (length < 1)
                return "";
            return NextString(length, allowedCharacters, length);
        }

        /// <summary>
        /// Returns a randomly generated string of a specified length, containing
        /// only a set of characters, and at max a specified number of non alpha numeric characters.
        /// </summary>
        /// <param name="length">length of the string</param>
        /// <param name="allowedCharacters">Characters allowed in the string</param>
        /// <param name="numberOfNonAlphaNumericsAllowed">Number of non alpha numeric characters allowed.</param>
        /// <returns>A randomly generated string of a specified length, containing only a set of characters, and at max a specified number of non alpha numeric characters.</returns>
        public virtual string NextString(int length, string allowedCharacters, int numberOfNonAlphaNumericsAllowed)
        {
            if (length < 1)
                return "";
            var TempBuilder = new StringBuilder();
            var Comparer = new Regex(allowedCharacters);
            var alphaNumbericComparer = new Regex("[0-9a-zA-z]");
            int Counter = 0;
            while (TempBuilder.Length < length)
            {
                var tempValue = new string(Convert.ToChar(Convert.ToInt32(Math.Floor(94*NextDouble() + 32))), 1);
                if (Comparer.IsMatch(tempValue))
                {
                    if (!alphaNumbericComparer.IsMatch(tempValue) && numberOfNonAlphaNumericsAllowed > Counter)
                    {
                        TempBuilder.Append(tempValue);
                        ++Counter;
                    }
                    else if (alphaNumbericComparer.IsMatch(tempValue))
                    {
                        TempBuilder.Append(tempValue);
                    }
                }
            }
            return TempBuilder.ToString();
        }

        /// <summary>
        /// Returns a random boolean value
        /// </summary>
        /// <returns>returns a boolean</returns>
        public virtual bool NextBool()
        {
            if (Next(0, 2) == 1)
                return true;
            return false;
        }

        /// <summary>
        /// Gets a random enum value
        /// </summary>
        /// <typeparam name="T">The enum type</typeparam>
        /// <returns>A random value from an enum</returns>
        public virtual T NextEnum<T>()
        {
            Array values = Enum.GetValues(typeof (T));
            int index = Next(0, values.Length);
            return (T) values.GetValue(index);
        }

        /// <summary>
        /// Randomly generates a new time span
        /// </summary>
        /// <param name="start">Start time span</param>
        /// <param name="end">end time span</param>
        /// <returns>A time span between the start and end</returns>
        public virtual TimeSpan NextTimeSpan(TimeSpan start, TimeSpan end)
        {
            if (start > end)
                throw new ArgumentException("The start value must be earlier than the end value");
            return start + new TimeSpan((long) (new TimeSpan(end.Ticks - start.Ticks).Ticks*NextDouble()));
        }
    }
}